-- UIHeroMain
-- Create by chengb Dec/31/2014
-- 英雄主界面

require "game/ui/form/hero/UIHeroInfo"
require "game/ui/form/hero/UIHeroUpgradeRank"
require "game/ui/form/equips/UIWeaponsExplain"
require "game/ui/form/skill/UISkillOperation"
require "game/ui/module/hero/HeroDisplay"
require "game/ui/form/item/UIItemCompound"
require "game/ui/form/item/UISlimeFoodInfo"

-- UIHeroMain继承自Layer
UIHeroMain = class("UIHeroMain", function()
    return cc.Layer:create();
end);

function UIHeroMain.create(classId, isSameCamp)
    return UIHeroMain.new(classId, isSameCamp);
end

-- 英雄属性类型
local HERO_LEVEL  = 1;    -- 等级
local HERO_ATTACK = 2;    -- 攻击
local HERO_MAGIC  = 3;    -- 魔力
local HERO_HP     = 4;    -- 生命
local HERO_MP     = 5;    -- 魔法

-- 技能格最大数量
local MAX_SKILL_GRID_NUM = 4;

-- 模型缩放比例
local MODEL_SCALE = 1

-- 内部函数声明
local resize;
local whenUpgradeRank;
local whenEquip;
local whenSkillOption;
local whenPetActivate;
local getNextDIdByRank;

-- 物品数量变化缓存
local itemUpdateQueue = {};

-- 构造函数
function UIHeroMain:ctor(classId, isSameCamp)
    -- 初始化
    self:setName("UIHeroMain");
    local node = cc.CSLoader:createNode("layout/hero/HeroMain.csb");
    self:addChild(node);
    self.node = node;

    -- 记录英雄id
    self.curHeroId = classId;
    EventMgr.fire(event.FEED_PET_SHOW_CHANGE, self.curHeroId);

    if isSameCamp then
        local campId = CampM.getPetCamp(self.curHeroId);
        self.showCampId = campId;
    end

    -- 注册事件处理回调函数
    self:registerEventCallback()

    -- 注册各种点击事件
    self:registerTouchEvent();

    -- 重绘
    self:redraw();

    self:changeArrowState();

    -- 适配
    resize(node);

    -- 初始化标示
    self.isInit = true;
end

-- 窗口出现动画
function UIHeroMain:playAppearEffect()
    local BOTTOM = findChildByName(self.node, "BOTTOM");
    local CT = findChildByName(self.node, "CT");
    local BT = findChildByName(self.node, "BT");
    local detailNode = findChildByName(self.node, "BT/detail_node");
    local weaponNode =  findChildByName(self.node, "BT/equip_frame/weapon_node");
    local medalNode = findChildByName(self.node, "BT/equip_frame/medal_node");
    local upgradeNode = findChildByName(self.node, "BT/upgrade_node");

    -- BOTTOM节点从左边边飞入，并略微回弹下
    self:playLeftInEffect(BOTTOM);

    -- DetailNode节点从左边边飞入，并略微回弹下
    self:playLeftInEffect(detailNode, 0.25, nil, 0.25);

    -- medalNode 从左边飞入，并略微回弹下
    self:playLeftInEffect(medalNode, 0.35);

    -- weaponNode 从左边飞入，并略微回弹下
    self:playLeftInEffect(weaponNode, 0.50, 5);

    -- upgradeNode 弹出
    local function callback()
        self:scaleUpEffect(upgradeNode);
    end
    upgradeNode:setScale(0);
    performWithDelay(self, callback, 0.80);

    -- CT节点从右边飞入，并略微回弹下
    local oldPos = cc.p(CT:getPosition());
    CT:setPosition(DESIGN_WIDTH, oldPos.y);
    local moveIn = cc.EaseIn:create(cc.MoveTo:create(0.35, cc.p(oldPos.x - 20, oldPos.y)), 1.8);
    local moveBack = cc.MoveTo:create(0.15, cc.p(oldPos.x, oldPos.y));
    CT:runAction(cc.Sequence:create(moveIn, moveBack));
end

-- 左边飞入函数
function UIHeroMain:playLeftInEffect(node, delay, offsetX, moveTime)
    local oldPos = cc.p(node:getPosition());
    local moveOff = offsetX or 20;
    node:setPosition(-DESIGN_WIDTH, oldPos.y);
    local moveIn = cc.EaseIn:create(cc.MoveTo:create(moveTime or 0.35, cc.p(oldPos.x + moveOff, oldPos.y)), 1.8);
    local moveBack = cc.MoveTo:create(0.15, cc.p(oldPos.x, oldPos.y));
    if delay then
        local delay = cc.DelayTime:create(delay);
        node:runAction(cc.Sequence:create(delay, moveIn, moveBack));
    else
        node:runAction(cc.Sequence:create(moveIn, moveBack));
    end
end

-- 注册事件处理回调函数
function UIHeroMain:registerEventCallback()
    -- 注册英雄升阶的回调
    EventMgr.register("UIHeroMain", event.UPGRADE_PET_RANK, function(args)
        whenUpgradeRank(self, args);
    end)

    -- 注册英雄升级（喂食成功）的回调
    EventMgr.register("UIHeroMain", event.PET_LEVEL_UP, function(args)
        local CT = findChildByName(self.node, "CT");
        if CT:isVisible() then
            self:whenLevelUp(args);
        end
    end);

    -- 注册属性升级的回调
    EventMgr.register("UIHeroMain", event.UPGRADE_PET_PROP, function(args)
        self:redrawBaseAttrib();
    end);

    -- 注册英雄穿上装备的回调
    EventMgr.register("UIHeroMain", event.EQUIP, function(args)
        whenEquip(self, args);
    end);

    -- 注册英雄穿上时装的回调
    EventMgr.register("UIHeroMain", event.UNEQUIP, function(args)
        local type = args.type;
        if type == EQUIP_TYPE_COSTUME then
            local petId = EquipM.query(args.classId, "dbase")["pet_id"];
            local layout = self.pageView:getPage(self.pageView:getCurPageIndex());
            self:updateHeroModel(layout, petId);

            self:redrawBaseAttrib();
        end
    end);

    -- 注册英雄佩戴技能的回调
    EventMgr.register("UIHeroMain", event.SET_SKILL_OPTION, function(args)
        whenSkillOption(self, args);
    end);

    -- 注册英雄出战的回调
    EventMgr.register("UIHeroMain", event.ACTIVE_PET, function(classId)
        whenPetActivate(self, classId);
    end);

    -- 注册事件
    EventMgr.register("UIHeroMain", event.ITEM_COUNT_UPDATED, function(classId)
        -- 先判断下当前焦点是否是自己
        if self.loseFocus then
            -- 缓存起来
            itemUpdateQueue[classId] = true;

            return;
        end

        self:whenItemUpdated(classId);
    end);

    -- 注册获得焦点的回调
    EventMgr.register("UIHeroMain", event.FOCUS_CHANGED, function(para)
        if para["get"] == "UIHeroMain" then
            UIMgr:getCurrentScene().topMenu:show(TOPMENU_FRAME_MONEY, TOPMENU_FRAME_FEED);

            -- 当前焦点
            self.loseFocus = false;
            for classId, _ in pairs(itemUpdateQueue) do
                self:whenItemUpdated(classId);
            end
            itemUpdateQueue = {};
        else
            -- 失去焦点
            self.loseFocus = true;
        end

        -- 非初始化获得焦点，BOTTOM向上
        if para["get"] == "UIHeroMain" and not self.isInit
           and (para["lose"] ~= "UIHeroMain" and para["lose"] ~= "UICommunicating")then
            self:bottomFlyUp();

        elseif para["lose"] == "UIHeroMain" and para["get"] ~= "UIHeroMain" and para["get"] ~= "UICommunicating" then
            -- 失去焦点BOTTOM隐藏
            self:hideBottom();
        end

        if self.isInit then
            self.isInit = false;
        end
    end);

    -- 界面析构后，需要清理下
    self:registerScriptHandler(function(ev)
        if ev == "exit" then
            cclog("UIHeroMain 界面析构清理");

            -- 反注册所有事件
            EventMgr.removeAll("UIHeroMain");

            -- 显示天赋界面
            local uiTalent = UIMgr.getCurrentScene():getFormByName("UITalentMain");
            if uiTalent ~= nil then
                uiTalent:setVisible(true);
            end

            -- SLIMEC-3283解决方法
            closeCommunicatingForm();
        elseif ev == "enter" then
            -- 播放窗口出现动画
            self:playAppearEffect();

            -- 窗口动画结束后，再显示左右箭头
            self.hideArrow = true;
            print("hide arrow")

            -- 检查时装功能
            self:checkReminder();

            local function showArrow()
                self.hideArrow = false;
                self:changeArrowState();

                local btnPrevious = findChildByName(self.node, "CT/mid/left_btn");
                local btnNext = findChildByName(self.node, "CT/mid/right_btn");
                self:fadeInEffect(btnPrevious);
                self:fadeInEffect(btnNext);
            end
            performWithDelay(self, showArrow, 1.2);

            -- 隐藏天赋界面
            local uiTalent = UIMgr.getCurrentScene():getFormByName("UITalentMain");
            if uiTalent ~= nil then
                uiTalent:setVisible(false);
            end
        end
    end);
end

-- 依次检查是否有提醒
function UIHeroMain:checkReminder()
    local checkList = { "costume", };

    local function callback(desc)
        local function okFunc()
            -- 继续检查是否还有其他提醒
            self:checkReminder();
        end

        confirm2(CONFIRM_TYPE_OK, getLocStr("slime_note"), desc, okFunc);
    end

    -- 检查进度
    if not self.checkProgress then
        self.checkProgress = 1;
    end

    print("self.checkProgress:"..self.checkProgress);
    for i = self.checkProgress, #checkList do
        local ret = FeatureItemM.checkReminder(checkList[i], callback);
        if ret == true then
            -- 每次检查一个提醒，确认后再继续
            self.checkProgress = i + 1;
            return;
        end
    end
end

-- 物品变化
function UIHeroMain:whenItemUpdated(classId)
    local type = ItemM.query(classId, "type");
    if type == ITEM_TYPE_FOOD or type == ITEM_TYPE_MATERIAL then
        -- 重绘喂食图标
        if self.redrawTickNode == nil then
            self.redrawTickNode = cc.Node:create();
            self:addChild(self.redrawTickNode);
        end

        local function delay()
            self:redrawFood();
        end
        self.redrawTickNode:stopAllActions();
        performWithDelay(self.redrawTickNode, delay, 0.1);
    end
end

-- 渐变出现效果
function UIHeroMain:fadeInEffect(node)
    node:setOpacity(0);
    local fadeIn = cc.FadeIn:create(0.5);
    node:runAction(fadeIn);
end

-- 播放弹出效果
function UIHeroMain:scaleUpEffect(node)
    local scaleUp = cc.ScaleTo:create(0.2, 1.2);
    local scaleDown = cc.ScaleTo:create(0.1, 1);
    local delay = cc.DelayTime:create(0.1);
    node:setScale(0);
    node:runAction(cc.Sequence:create(delay, scaleUp, scaleDown));
end

-- 注册点击各种事件
function UIHeroMain:registerTouchEvent()
    local node = self.node;
    local BT = node:getChildByName("BT");
    local CT = node:getChildByName("TOP");

    -- 注册返回按钮点击事件
    local btnBack = findChildByName(self.node, "BOTTOM/back_btn");
    local function onBackClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            -- 关闭当前界面
            AudioM.playFx("button_return");

            -- 先隐藏按钮
            self.btnNext:setVisible(false);
            self.btnPrevious:setVisible(false);

            UIMgr.getCurrentScene():removeFormByName("UIHeroMain");
            -- printVisibleTextures(self);
        end
    end
    AddBackEventHandler(self, btnBack, onBackClick);
    TextStyleM.setTextStyle(btnBack, TextStyleM.TEXT_SIZE_MIDDLE, TextStyleM.TEXT_COLOR_BROWN_YELLOW, true);
    btnBack:setTitleText(getLocStr("btn_text_back"));

    -- 注册确认按点击事件
    local btnOk = findChildByName(self.node, "BOTTOM/ok_btn");
    local function onOkClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            AudioM.playFx("button_click");

            -- 如果是暂离状态，
            if DungeonM.isInDungeon() then
                alert(getLocStr("cant_active_pet"));
                return;
            end

            -- 设置不变， 不处理
            if not self:isSettingChange() then
                return;
            end
            -- 保存设置
            self:saveSetting();
        end
    end
    btnOk:addTouchEventListener(onOkClick);
    TextStyleM.setTextStyle(btnOk, TextStyleM.TEXT_SIZE_MIDDLE, TextStyleM.TEXT_COLOR_BROWN_YELLOW, true);
    btnOk:setTitleText(getLocStr("btn_text_activate_pet"));

    -- 注册介绍按钮点击事件
    local btnIntro = findChildByName(self.node, "BOTTOM/intro_btn");
    local function onIntroClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            AudioM.playFx("button_click");
            -- 打开英雄信息界面
            self:showHeroInfo();
            AudioM.playFx("button_caption");
        end
    end
    btnIntro:addTouchEventListener(onIntroClick);
    TextStyleM.setTextStyle(btnIntro, TextStyleM.TEXT_SIZE_MIDDLE, TextStyleM.TEXT_COLOR_BROWN_YELLOW, true);

    if ServerStatusM.query(MODULE_SWITCH_OPEN_COSTUME) ~= 1 or not FeatureItemM.isFeatureOpen("costume") then
        btnIntro:setTitleText(getLocStr("btn_text_info2"));
    else
        btnIntro:setTitleText(getLocStr("btn_text_costume"));
    end

    -- 注册升阶按钮点击事件
    local upgradeFrame = findChildByName(self.node, "BT/upgrade_node/frame");
    local function onUpgradeRankClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            local pet = PetM.getMyPet(self.curHeroId);
            local ret = PetM.canUpgradeRank(self.curHeroId);
            local openType = "upgrade";
            if ret == "current_max_rank" then
                if pet:getRank() >= pet:query("max_rank") then
                    -- 确实是满阶位了
                    alert(getLocStr("already_full_rank_tip"));
                else
                    local parentId = getNextDIdByRank(pet:getRank());

                    alert(string.format(getLocStr("already_max_rank_tip"), DungeonAreaM.query(parentId, "name")));
                end
                return;
            elseif ret == "rank_upper" and AwakeM.isAwakeSystemOpen() then
                -- 达到最大阶位，显示觉醒相关界面
                if AwakeM.isCarved(self.curHeroId) then
                    -- 打开雕像信息界面
                    openCarvingInfoDialog(self.curHeroId);
                    return;
                else
                    -- 打开快捷界面
                    if AwakeM.isFinishAwakeDungeon(self.curHeroId) and AwakeM.isFinishGuide() then
                        -- 已完成地牢试炼，打开雕像快捷界面
                        openType = "awake_carving";
                    elseif AwakeM.getGuideProgress() >= 2 then
                        -- 未完成地牢试炼，打开地牢试炼快捷界面
                        openType = "awake_dungeon";
                    end
                end
            end
            -- 打开升阶界面
            if UIMgr.getCurrentScene():isOpen("UIHeroUpgradeRank") then
                UIMgr.getCurrentScene():removeFormByName("UIHeroUpgradeRank");
            end
            local uiHeroUpgradeRank = UIHeroUpgradeRank.create(self.curHeroId, openType);
            UIMgr.getCurrentScene():addForm(uiHeroUpgradeRank);
            AudioM.playFx("ui_open1");
        end
    end
    upgradeFrame:addTouchEventListener(onUpgradeRankClick);

    local pageView = findChildByName(self.node, "CT/mid/page_view");

    -- 自定义滑动翻页的阀值，默认是整个页面宽度的一半, 改为100像素
    pageView:setCustomScrollThreshold(100);
    pageView:setUsingCustomScrollThreshold(true);

    self.pageView = pageView;

    local btnPrevious = findChildByName(self.node, "CT/mid/left_btn");
    self.btnPrevious = btnPrevious;
    local btnNext = findChildByName(self.node, "CT/mid/right_btn");
    self.btnNext = btnNext;

    -- 放大左右按钮区域
    local panelLeft = findChildByName(self.node, "CT/mid/panel_left");
    self.panelLeft = panelLeft;
    panelLeft:setTouchEnabled(true);
    panelLeft:addTouchEventListener(function(sender,eventType)
        if eventType == ccui.TouchEventType.began then
            btnPrevious:setHighlighted(true);
        elseif eventType == ccui.TouchEventType.ended then
            btnPrevious:setHighlighted(false);

            AudioM.playFx("button_tiny");
            local total = self:getHeroNumber();
            if total <= 1 then
                return;
            end

            local curPageIndex = pageView:getCurPageIndex();

            -- 隐藏喂食节点
            self:hideFoodNode();

            -- 切换至上一页
            self:switchPage(curPageIndex - 1);
        else
            btnPrevious:setHighlighted(false);
        end
    end);

    local panelRight = findChildByName(self.node, "CT/mid/panel_right");
    self.panelRight = panelRight;
    panelRight:setTouchEnabled(true);
    panelRight:addTouchEventListener(function(sender,eventType)
        if eventType == ccui.TouchEventType.began then
            btnNext:setHighlighted(true);
        elseif eventType == ccui.TouchEventType.ended then
            btnNext:setHighlighted(false);

            AudioM.playFx("button_tiny");
            local total = self:getHeroNumber();
            if total <= 1 then
                return;
            end

            local curPageIndex = pageView:getCurPageIndex();

            -- 隐藏喂食节点
            self:hideFoodNode();

            -- 切换至下一页
            self:switchPage(curPageIndex + 1);
        else
            btnNext:setHighlighted(false);
        end
    end);

    local detailNode = findChildByName(BT, "detail_node");

    -- 攻击
    local attackImg = findChildByName(detailNode, "attack_node/bg");
    attackImg:setTag(HERO_ATTACK);

    -- 魔力
    local magicImg = findChildByName(detailNode, "magic_node/bg");
    magicImg:setTag(HERO_MAGIC);

    -- 生命
    local hpImg = findChildByName(detailNode, "hp_node/bg");
    hpImg:setTag(HERO_HP);

    -- 魔法
    local mpImg = findChildByName(detailNode, "mp_node/bg");
    mpImg:setTag(HERO_MP);

    -- 注册武器合成按钮点击事件
    local weaponImage      = findChildByName(BT, "equip_frame/weapon_node/icon");
    local weaponImageFrame = findChildByName(BT, "equip_frame/weapon_node/frame");
    local equipWeapon      = findChildByName(BT, "equip_frame/weapon_node/can_equip");
    local function OnWeaponCompoundClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            if sender.classId then
                -- 显示武器/徽章详情界面
                if UIMgr.getCurrentScene():isOpen("UIItemDesc") then
                    UIMgr.getCurrentScene():removeFormByName("UIItemDesc");
                end

                local uiUIWeaponsExplain = UIItemDesc.create(sender.classId);
                UIMgr.getCurrentScene():addForm(uiUIWeaponsExplain);
                AudioM.playFx("equip_put");
            else
                local pet = PetM.getMyPet(self.curHeroId);
                local info = {};
                info.pet = pet;
                info.type = EQUIP_TYPE_WEAPON;

                -- 如果获取不到升阶所需的武器或者所需武器的阶位为1，则不显示合成界面
                local ret = PetM.getUpgradeRankEquip(pet, EQUIP_TYPE_WEAPON);
                if self:equipWeapons(pet, ret[1], EQUIP_TYPE_WEAPON) then
                    return;
                end

                -- 出战和链接的不允许佩戴
                if PetM.pauseDungeonLimit(self.curHeroId) then
                    if PetM.isActivePet(self.curHeroId) then
                        alert(getLocStr("active_no_weapon"));
                    else
                        alert(getLocStr("lictor_no_weapon"));
                    end
                    return;
                end

                -- 显示合成界面
                UIMgr.getCurrentScene():removeFormByName("UIItemCompound");
                local UIItemCompound = UIItemCompound.create(self.curHeroId, ret[1]);
                UIMgr.getCurrentScene():addForm(UIItemCompound);
                AudioM.playFx("ui_open1");
            end
        end
    end
    weaponImage:addTouchEventListener(OnWeaponCompoundClick)
    weaponImageFrame:addTouchEventListener(OnWeaponCompoundClick)

    -- 注册徽章合成按钮点击事件
    local btnMedal      = findChildByName(BT, "equip_frame/medal_node/icon");
    local btnMedalFrame = findChildByName(BT, "equip_frame/medal_node/frame");
    local equipMedal    = findChildByName(BT, "equip_frame/medal_node/can_equip");
    local function onMedalClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            if sender.classId then
                -- 显示武器/徽章详情界面
                if UIMgr.getCurrentScene():isOpen("UIItemDesc") then
                    UIMgr.getCurrentScene():removeFormByName("UIItemDesc");
                end

                local uiUIWeaponsExplain = UIItemDesc.create(sender.classId);
                UIMgr.getCurrentScene():addForm(uiUIWeaponsExplain);
                AudioM.playFx("button_spell");
            else
                local pet = PetM.getMyPet(self.curHeroId);
                local info = {};
                info.pet = pet;
                info.type = EQUIP_TYPE_MEDAL;

                local ret = PetM.getUpgradeRankEquip(pet, EQUIP_TYPE_MEDAL);
                if self:equipWeapons(pet, ret[1], EQUIP_TYPE_MEDAL) then
                    return;
                end

                -- 出战和链接的不允许佩戴
                if PetM.pauseDungeonLimit(self.curHeroId) then
                    if PetM.isActivePet(self.curHeroId) then
                        alert(getLocStr("active_no_medal"));
                    else
                        alert(getLocStr("lictor_no_medal"));
                    end
                    return;
                end

                -- 显示合成界面
                UIMgr.getCurrentScene():removeFormByName("UIItemCompound");
                local UIItemCompound = UIItemCompound.create(self.curHeroId, ret[1]);
                UIMgr.getCurrentScene():addForm(UIItemCompound);
                AudioM.playFx("ui_open1");
            end
        end
    end
    btnMedal:addTouchEventListener(onMedalClick);
    btnMedalFrame:addTouchEventListener(onMedalClick);

    -- 注册喂食图片点击事件
    local dialogImg = findChildByName(self.node, "CT/food_node/dialog");
    dialogImg:setTouchEnabled(true);
    local function onFoodClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            if not PetM.getFeedFood(self.curHeroId) then
                -- 如果获取不到食物信息（可能是已经满级了），不处理
                return;
            end

            -- 迷宫暂离时，当前冈布奥是出战冈布奥(不可喂食)（SLIMEC-3367）
            local activeHero = ME.user:getActivePet();
            if DungeonM.isInDungeon() and activeHero.classId == self.curHeroId then
                alert(getLocStr("cant_feed_slime"));
                return;
            end

            -- 打开合成界面
            UIMgr.getCurrentScene():removeFormByName("UIItemCompound");
            local UISlimeFoodInfo = UISlimeFoodInfo.create(self.curHeroId);
            UIMgr.getCurrentScene():addForm(UISlimeFoodInfo);
            AudioM.playFx("ui_open1");
        end
    end
    dialogImg:addTouchEventListener(onFoodClick);

    -- 注册专属技能图标点击事件
    local skillImg = findChildByName(self.node, "BT/detail_node/skill_node/icon");
    skillImg:setTouchEnabled(true);
    local function onSkillClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            AudioM.playFx("button_caption");
            -- 显示技能操作页面
            if UIMgr.getCurrentScene():isOpen("UISkillOperation") then
                UIMgr.getCurrentScene():removeFormByName("UISkillOperation");
            end

            local uiSkillOperation = UISkillOperation.create(sender.skillId);
            UIMgr.getCurrentScene():addForm(uiSkillOperation);
        end
    end
    skillImg:addTouchEventListener(onSkillClick);
end

-- 装备武器
function UIHeroMain:equipWeapons(pet, classId, type)
    local count = ItemM.getAmount(ME.user,classId);
    if count <= 0 then
        return false;
    end

    local okFunc = function()
        -- 请求装备
        Operation.cmd_hero_equip(pet, classId);
    end

    local name = ItemM.query(classId, "name");
    local tipTxt = string.format(getLocStr("equip_weapons"), name);
    confirm(tipTxt, okFunc);

    return true;
end

-- 翻页
function UIHeroMain:switchPage(toPageIndex)
    local pageView = self.pageView;
    pageView:scrollToPage(toPageIndex);
end

-- 适配
function resize(node)
    local w = AlignM.frameSize.width;
    local h = AlignM.frameSize.height;

    -- bg居中显示并充满整个屏幕
    local bg = findChildByName(node, "bg");
    bg:setPosition(w / 2, h / 2);
    bg:setScaleX(w / DESIGN_WIDTH);
    bg:setScaleY(h / 1136);

    local bg2 = findChildByName(node, "bg2");
    bg2:setPosition(w / 2, h / 2);
    bg2:setScaleX(w / DESIGN_WIDTH);
    bg2:setScaleY(h / 1136);

    -- TOP顶部居中并缩放至合适大小
    AlignM.alignToTopCenter(node, "TOP");

    -- CT居中并缩放至合适大小
    AlignM.alignToCenter(node, "CT");

    -- mask居中并缩放至合适大小
    AlignM.alignToCenter(node, "mask");

    -- BT置于底部并缩放至合适大小
    AlignM.alignToBottomCenter(node, "BT");

    -- BOTTOM置于底部并缩放至合适大小
    AlignM.alignToBottomCenter(node, "BOTTOM");
end

----------------------------
-- 重绘
-- @param isSwithPage 是否是翻页
function UIHeroMain:redraw(isSwithPage)
    -- 获取当前页的动画节点
    local layout = self.pageView:getPage(self.pageView:getCurPageIndex());
    local animaNode = nil;
    if layout ~= nil then
        animaNode = layout.animaNode;
    end

    if not animaNode and not isSwithPage then
        self.loadAnimIndex = self.curHeroId;
        -- 加载动画
        showCommunicatingForm(getLocStr("loading"));
    end

    local CT = self.node:getChildByName("TOP");

    -- 显示英雄名称（需要描边）
    local nameLabel = findChildByName(CT, "name_bg/name_text");
    local heroConfig = PetM.query(self.curHeroId);
    nameLabel:setString(PetM.query(self.curHeroId, "name"));
    TextStyleM.setArtWordTitleStyle(nameLabel);

    -- 如果不是翻页，则重绘英雄模型
    -- 注：翻页时滚动容器已经自动切换好模型了，所以这里无需重绘
    if not isSwithPage then
        self:redrawModel();
    end

    -- 重绘英雄星级
    self:redrawHeroStar();

    -- 显示装备和徽章
    self:redrawEquip();

    -- 显示天赋信息
    self:redrawTalent();

    -- 显示基础属性
    self:redrawBaseAttrib();

    -- 显示技能
    self:redrawSkill();

    -- 重绘喂食图标
    self:redrawFood();

    -- 设置是否显示出战按钮
    local btnOk = findChildByName(self.node, "BOTTOM/ok_btn");
    local activeHero = ME.user:getActivePet();
    if activeHero.classId == self.curHeroId then
        btnOk:setVisible(false);
    else
        btnOk:setVisible(true);
    end

    -- 冈布奥是否在升级中，升级中出战按钮不可用
    if TrainingM.isUpgrading(self.curHeroId) or
        AwakeM.isCarving(self.curHeroId) then
        btnOk:setTouchEnabled(false);
        btnOk:setBright(false);
    else
       btnOk:setTouchEnabled(true);
       btnOk:setBright(true);
    end

    --  播放英雄出场音效
    local modelId = activeHero:getModelId();
    local audio = PetM.queryModel(modelId, "appear_audio");
    if type(audio) == 'string' then
        AudioM.playFx(audio);
    end

    -- 根据冈布奥的阵营重绘背景
    local bgNode = findChildByName(self.node, "bg");
    local campId = CampM.getPetCamp(self.curHeroId);

    if self.heroCampId ~= campId then
        -- 重新加载背景图，并更新阵营ID的记录
        self.heroCampId = campId;
        local campIconPath = getHeroBgIconPath(campId);

        if not bgNode.hasBg then
            -- 第一次直接加载
            bgNode:loadTexture(campIconPath);
            bgNode.hasBg = true;
        else
            local function onImageLoaded()
                -- 新的背景和老背景交替
                local bg2Node = findChildByName(self.node, "bg2");
                bg2Node:loadTexture(campIconPath);
                bg2Node:setVisible(true);
                bg2Node:setOpacity(0);
                bgNode:setOpacity(255);

                local fadeIn = cc.FadeIn:create(0.5);
                local fadeOut = cc.FadeOut:create(0.5);
                local callFunc = cc.CallFunc:create(function()
                    bgNode:loadTexture(campIconPath);
                    bgNode:setOpacity(255);
                    bg2Node:setVisible(false);
                end);

                bg2Node:stopAllActions();
                bg2Node:runAction(fadeIn);
                bgNode:stopAllActions();
                bgNode:runAction(cc.Sequence:create(fadeOut, callFunc));
            end
            cc.Director:getInstance():getTextureCache():addImageAsync(campIconPath, onImageLoaded);
        end
    end
end

-- 重绘英雄星级
function UIHeroMain:redrawHeroStar()
    local starNode = findChildByName(self.node, "TOP/star_node");
    local hero = PetM.getMyPet(self.curHeroId);
    if not hero then
        cclog("英雄对象不存在(class_id = %s)", tostring(self.curHeroId));
        return;
    end

    -- 获取英雄阶位
    local starNum = hero:getRank();
    local maxStarNum = TrainingM.getLevel() + 1;
    if maxStarNum < 1 then
        maxStarNum = 1;
    end
    if maxStarNum < starNum then
        maxStarNum = starNum;
    end

    -- 生成星星层，直接挂到结点上
    local lightStarPath = "images/ui/symbol/star_large.png";
    local darkStarPath  = "images/ui/symbol/star_large2.png";

    -- 如果冈布奥已觉醒，使用橙色的星星
    if AwakeM.isCarved(self.curHeroId) then
        lightStarPath = "images/ui/symbol/star_large3.png";
    end

    local layer = createStarLayerEx(starNum, maxStarNum, lightStarPath, darkStarPath);
    if not layer then
        return;
    end
    starNode:removeAllChildren();
    starNode:addChild(layer);

    -- 升阶按钮也要根据阶位显示
    local upgradeNode = findChildByName(self.node, "BT/upgrade_node");

    local iconImg = findChildByName(upgradeNode, "icon");
    iconImg:removeAllChildren();
    iconImg:stopAllActions();

    local pet = PetM.getMyPet(self.curHeroId);
    local upRank = pet:getRank() >= pet:query("max_rank");
    local featureOpen = FeatureItemM.isFeatureOpen("awake");

    if not FeatureItemM.isFeatureOpen("heart") or
        TrainingM.getLevel() == 0 or
        (upRank and not featureOpen) or
        (upRank and featureOpen and not AwakeM.isAwakeSystemOpen()) then
        upgradeNode:setVisible(false);
    else
        upgradeNode:setVisible(true);

        if upRank and featureOpen then
            -- 达到最大阶位，显示觉醒相关界面
            if AwakeM.isCarved(self.curHeroId) then
                -- 已建造雕像的话，显示雕像
                iconImg:loadTexture(getAwakeIconPath("carved"));
                iconImg:setScale(0.9);
                setGrayMode(iconImg, false);

                shiningEffect(iconImg, 45, 50);
            else
                local awakeProgress = AwakeM.getGuideProgress();
                -- 打开快捷界面
                if AwakeM.isFinishAwakeDungeon(self.curHeroId) then
                    -- 已完成地牢试炼，如果完成了雕像指引。则显示雕像
                    if AwakeM.isFinishGuide() then
                        iconImg:loadTexture(getAwakeIconPath("carving"));
                        iconImg:setScale(0.9);
                        iconImg.grayMode = nil;
                        setGrayMode(iconImg, true);
                    else
                        upgradeNode:setVisible(false);
                    end
                else
                    if AwakeM.getGuideProgress() >= 2 then
                        -- 未完成地牢试炼，显示地牢试炼
                        iconImg:loadTexture(getAwakeIconPath("awake_dungeon"));
                        iconImg:setScale(1);
                        iconImg.grayMode = nil;
                        setGrayMode(iconImg, true);
                    else
                        upgradeNode:setVisible(false);
                    end
                end
            end
        else
            iconImg:loadTexture(getUIHeroIconPath("upgrade_rank"));
            iconImg:setScale(1);
            setGrayMode(iconImg, false);
        end
    end
end

-- 显示英雄技能
function UIHeroMain:redrawSkill()
    -- 显示专属技能图片
    local skillNode = findChildByName(self.node, "BT/detail_node/skill_node");
    local hero = PetM.getMyPet(self.curHeroId);
    local specialSkill = hero:getSpecial();
    specialSkill = FormulaM.invoke("CALC_FINAL_SKILL", hero, specialSkill);
    local skillImg = findChildByName(skillNode, "icon");
    local icon = SkillM.query(specialSkill, "icon");
    skillImg:loadTexture(getSkillIconPath(icon));
    skillImg.skillId = specialSkill;

    local skillBg = findChildByName(skillNode, "skill_bg");
    local rotateForever = cc.RepeatForever:create(cc.RotateBy:create(30, 360));
    skillBg:runAction(rotateForever);

    -- 显示专属技能等级
    local level = SkillM.query(specialSkill, "level");
    local levelLabel = findChildByName(skillNode, "level_label");
    local kerning = -6;
    if not isFullWidthLang() then
        kerning = -4;
    end
    TextStyleM.setTextStyle(levelLabel, TextStyleM.TEXT_SIZE_MIDDLE, TextStyleM.TEXT_COLOR_LIGHT_BLUE, true, kerning);
    levelLabel:setString(getLevelStr(level))
    TextStyleM.setOutlineStyle(levelLabel, 3);
--    for i = 1, MAX_SKILL_LEVEL do
--        local pointImg = findChildByName(skillNode, "point" .. i);
--        if i <= level then
--            pointImg:loadTexture("images/ui/symble/sign_pot1.png");
--        else
--            pointImg:loadTexture("images/ui/symble/sign_pot2.png");
--        end
--    end
end

-- 重绘基础属性
function UIHeroMain:redrawBaseAttrib()
    -- 获取英雄对象
    local hero = PetM.getMyPet(self.curHeroId);

    local detailNode = findChildByName(self.node, "BT/detail_node");
    local valueLabel, nameLabel;

    -- 攻击
    nameLabel = findChildByName(detailNode, "attack_node/name");
    valueLabel= findChildByName(detailNode, "attack_node/value");
    TextStyleM.setTextStyle(nameLabel, TextStyleM.TEXT_SIZE_SMALL2, TextStyleM.TEXT_COLOR_BEIGE, false, -2);
    TextStyleM.setTextStyle(valueLabel, TextStyleM.TEXT_SIZE_SMALL2, TextStyleM.TEXT_COLOR_BEIGE, false, -2);
    nameLabel:setString(getLocStr("attack"));
    valueLabel:setString(tostring(hero:getAttack()));

    -- 生命
    local nameLabel= findChildByName(detailNode, "hp_node/name");
    local valueLabel= findChildByName(detailNode, "hp_node/value");
    TextStyleM.setTextStyle(nameLabel, TextStyleM.TEXT_SIZE_SMALL2, TextStyleM.TEXT_COLOR_BEIGE, false, -2);
    TextStyleM.setTextStyle(valueLabel, TextStyleM.TEXT_SIZE_SMALL2, TextStyleM.TEXT_COLOR_BEIGE, false, -2);
    nameLabel:setString(getLocStr("hp"));
    valueLabel:setString(tostring(hero:getHp()));

    -- 魔力
    local nameLabel= findChildByName(detailNode, "magic_node/name");
    local valueLabel= findChildByName(detailNode, "magic_node/value");
    TextStyleM.setTextStyle(nameLabel, TextStyleM.TEXT_SIZE_SMALL2, TextStyleM.TEXT_COLOR_BEIGE, false, -2);
    TextStyleM.setTextStyle(valueLabel, TextStyleM.TEXT_SIZE_SMALL2, TextStyleM.TEXT_COLOR_BEIGE, false, -2);
    nameLabel:setString(getLocStr("magic"));
    valueLabel:setString(tostring(hero:getMagic()));

    -- 魔法
    local nameLabel= findChildByName(detailNode, "mp_node/name");
    local valueLabel= findChildByName(detailNode, "mp_node/value");
    TextStyleM.setTextStyle(nameLabel, TextStyleM.TEXT_SIZE_SMALL2, TextStyleM.TEXT_COLOR_BEIGE, false, -2);
    TextStyleM.setTextStyle(valueLabel, TextStyleM.TEXT_SIZE_SMALL2, TextStyleM.TEXT_COLOR_BEIGE, false, -2);
    nameLabel:setString(getLocStr("mp"));
    valueLabel:setString(tostring(hero:getMp()));
end

-- 重绘装备和徽章
function UIHeroMain:redrawEquip()
    -- 获取英雄对象
    local pet = PetM.getMyPet(self.curHeroId);

    -- 获取已经穿戴的武器
    local weaponClassId = EquipM.getHeroEquip(pet, EQUIP_TYPE_WEAPON, 1);
    local weaponImage = findChildByName(self.node, "BT/equip_frame/weapon_node/icon");
    local weaponFrame = findChildByName(self.node, "BT/equip_frame/weapon_node/frame");
    local upBtn = findChildByName(self.node, "BT/equip_frame/weapon_node/can_equip");
    weaponImage.classId = nil;
    weaponFrame.classId = nil;
    upBtn.classId = nil;

    -- 停止动画播放
    weaponImage:stopAllActions();
    weaponImage:removeAllChildren();
    setGrayMode(weaponImage, false);

    if weaponClassId then
        local iconIndex = ItemM.query(weaponClassId, "icon");
        weaponImage:loadTexture(getItemIconPath(iconIndex));

        weaponImage.classId = weaponClassId;
        weaponFrame.classId = weaponClassId;
        upBtn.classId = weaponClassId;

        -- 获取可穿戴提示控件 并隐藏
        weaponFrame:setVisible(true);
        weaponImage:setVisible(true);
        upBtn:setVisible(false);
    else
        -- 获取需要合成的装备
        local needEquipClassId = PetM.getUpgradeRankEquip(pet, EQUIP_TYPE_WEAPON);
        if #needEquipClassId == 0 then
            error(string.format("获取不到宠物(%d)升阶所需的武器或徽章。", self.curHeroId));
        else
            local iconIndex = ItemM.query(needEquipClassId[1], "icon");
            weaponImage:loadTexture(getItemIconPath(iconIndex));
            -- 获取物品合成规则
            local count = PetM.isMaterialEnough(self.curHeroId, "weapon");
            if count ~= true then
                -- 获取可穿戴提示控件 并隐藏
                weaponFrame:setVisible(true);
                weaponImage:setVisible(true);
                upBtn:setVisible(false);

                setGrayTransMode(weaponImage, true);
            else
                -- 隐藏原装备框，显示可穿戴图标，并播放提示效果
                weaponFrame:setVisible(true);
                weaponImage:setVisible(false);
                upBtn:setVisible(true);

                playCanEquipHintEffect(upBtn, 0, 0);
            end
        end
    end

    -- 获取已经穿戴的徽章
    local medalClassId = EquipM.getHeroEquip(pet, EQUIP_TYPE_MEDAL, 1);
    local medalImage = findChildByName(self.node, "BT/equip_frame/medal_node/icon");
    local medalFrame = findChildByName(self.node, "BT/equip_frame/medal_node/frame");
    local upBtn = findChildByName(self.node, "BT/equip_frame/medal_node/can_equip");

    -- 停止动画播放
    medalImage:stopAllActions();
    medalImage:removeAllChildren();
    setGrayMode(medalImage, false);

    medalImage.classId = nil;
    medalFrame.classId = nil;
    upBtn.classId = nil;

    if medalClassId then
        local iconIndex = ItemM.query(medalClassId, "icon");
        medalImage:loadTexture(getItemIconPath(iconIndex));

        medalImage.classId = medalClassId;
        medalFrame.classId = medalClassId;
        upBtn.classId = medalClassId;

        -- 获取可穿戴提示控件 并隐藏
        medalFrame:setVisible(true);
        medalImage:setVisible(true);
        upBtn:setVisible(false);
    else
        -- 获取需要合成的装备
        local needEquipClassId = PetM.getUpgradeRankEquip(pet, EQUIP_TYPE_MEDAL);
        if #needEquipClassId == 0 then
            error(string.format("获取不到宠物(%d)升阶所需的武器或徽章。", self.curHeroId));
            return;
        end

        -- 获取物品合成规则
        local count = PetM.isMaterialEnough(self.curHeroId, "medal");
        if count ~= true then
            -- 获取可穿戴提示控件 并隐藏
            local iconIndex = ItemM.query(needEquipClassId[1], "icon");
            medalImage:loadTexture(getItemIconPath(iconIndex));

            medalFrame:setVisible(true);
            medalImage:setVisible(true);
            upBtn:setVisible(false);

            setGrayTransMode(medalImage, true);
        else
            -- 隐藏原装备框，显示可穿戴图标，并播放提示效果
            medalFrame:setVisible(true);
            medalImage:setVisible(false);
            upBtn:setVisible(true);

            playCanEquipHintEffect(upBtn, 0, 0);
        end
    end

    self.lastWeaponClassId = weaponClassId;
    self.lastMedalClassId = medalClassId;
end

-- 重绘天赋信息
function UIHeroMain:redrawTalent()
    local detailNode = findChildByName(self.node, "BT/detail_node");

    -- 显示天赋名称
    local talentNameLabel = findChildByName(detailNode, "talent_name");
    local hero = PetM.getMyPet(self.curHeroId);
    local skillId = hero:getTalentSkillId();
    local level = SkillM.query(skillId, "level");
    local name  = SkillM.query(skillId, "name");
    TextStyleM.setTextStyle(talentNameLabel, TextStyleM.TEXT_SIZE_SMALL, TextStyleM.TEXT_COLOR_DARK_RED);
    talentNameLabel:setString(string.format("%s Lv.%d", name, level));

    -- 显示天赋作用
    local talentDescLabel = findChildByName(detailNode, "talent_desc")
    local desc = SkillM.getSkillDesc(skillId);

    TextStyleM.setTextStyle(talentDescLabel, TextStyleM.TEXT_SIZE_SMALL2, TextStyleM.TEXT_COLOR_DARK_BROWN, nil, nil, nil, nil, nil, nil, nil, TextStyleM.getTextScale(desc, 19 * 3 * 2));
    applyString(talentDescLabel, desc, nil, nil, TextStyleM.TEXT_SIZE_SMALL2);
end

-- 获取可以显示的宠物
function UIHeroMain:getShowHero()
    local showHero = {};
    local heros;

    if self.showCampId then
        heros = CampM.getCampMyPetIds(self.showCampId);
    else
        heros = table.keys(ME.user.pets);
    end

    for _, id in pairs(heros) do
        if not TrainingM.isUpgrading(id) and
            not AwakeM.isCarving(id) then
            table.insert(showHero, id);
        end
    end

    return showHero;
end

-- 获取英雄数量
function UIHeroMain:getHeroNumber()
    local heros = self:getShowHero();
    return table.getn(heros);
end

-- 获取左边的英雄索引（循环获取）
function UIHeroMain:getLeftPageIndex(midPageIndex)
    local leftPage = -1;
    if midPageIndex > 1 then
        leftPage = midPageIndex - 1;
    elseif midPageIndex == 1 then
        -- 到最左边后，取最后一个
        leftPage = #self.heros;
    end
    return leftPage;
end

-- 获取右边的英雄索引（循环获取）
function UIHeroMain:getRightPageIndex(midPageIndex)
    local rightPage = -1;
    if midPageIndex < #self.heros then
        rightPage = midPageIndex + 1;
    elseif midPageIndex == #self.heros then
        -- 到最右边后，取第一个
        rightPage = 1;
    end
    return rightPage;
end

-- 更新英雄模型
function UIHeroMain:updateHeroModel(layout, id)
    layout:stopAllActions();
    layout:removeAllChildren();

    local imageView;

    local pet = PetM.getMyPet(id);
    local modelId = pet:getModelId();
    local animName = PetM.queryModel(modelId, "animation");
    -- if not animName or string.len(animName) == 0 then
    if true then
        -- 未配置动画，默认显示图片
        imageView = ccui.ImageView:create();
        imageView:loadTexture(getHeroLargeIconPath(pet:getLargeIcon()));

        local petScaleAndPos = PetM.getPetScaleAndPos(id, "main");

        imageView:setScale(imageView:getScale() * petScaleAndPos["scale"]);

        imageView:setPosition(cc.p(layout:getContentSize().width / 2, layout:getContentSize().height / 2));
        imageView:setPositionX(imageView:getPositionX() + petScaleAndPos["offset_x"]);
        imageView:setPositionY(imageView:getPositionY() + petScaleAndPos["offset_y"]);

        layout:addChild(imageView);
    end

    local function loadAnim()
        local function onAnimationImageLoaded()
            local form = UIMgr.getCurrentScene():getFormByName("UIHeroMain");
            if form == nil then
                print("invalid layout, skip");
                if self.actionPet == id then
                    self.actionPet = nil;
                end
                return;
            end

            if not animName or string.len(animName) == 0 then
                if self.actionPet == id then
                    self.actionPet = nil;
                end
                return;
            end

            if layout == nil then
                return;
            end

            -- 报错保护，判断是否已经从父节点中移除了
            local remove = true;
            local pageView = findChildByName(self.node, "CT/mid/page_view");
            for _, page in pairs(pageView:getPages()) do
                if page == layout then
                    remove = false;
                    break;
                end
            end
            if remove then
                return;
            end

            -- 调整冈布奥预加载静态图位置时，可以将该行注释掉
            layout:removeChild(imageView);

            -- 显示动画
            local animaNode = HeroDisplay.create(modelId);

            --TODO 调整冈布奥首帧静态图位置用，暂时保留
            -- animaNode.animation.action:gotoFrameAndPause(1);

            -- 注册节点的点击事件
            layout:setTouchEnabled(true);
            layout:addTouchEventListener(function(sender,eventType)
                if eventType == ccui.TouchEventType.ended then
                    -- 隐藏喂食节点
                    self:hideFoodNode();

                    -- 播放受创动画（需要播放音效）
                    animaNode:switchAction(HERO_ACTION_DAMAGED, true);
                end
            end);

            animaNode:setScale(MODEL_SCALE);
            animaNode:setPosition(cc.p(layout:getContentSize().width / 2, layout:getContentSize().height / 2))
            layout:addChild(animaNode);
            layout.animaNode = animaNode;

            if self.actionPet and self.actionPet == id then
                animaNode:switchAction(HERO_ACTION_CAST_ME, true);
                self.actionPet = nil;
            end

            if self.loadAnimIndex == id then
                closeCommunicatingForm();
            end
        end

        cc.Director:getInstance():getTextureCache():addImageAsync("images/slime_animation/"..animName..".png", onAnimationImageLoaded);
    end
    performWithDelay(layout, loadAnim, 1);
end

-- 创建页面
function UIHeroMain:createPage(index)
    local pageWidth = 640;
    local pageHeight = 500;

    local id = self.heros[index];

    local layout = ccui.Layout:create();
    layout:setTag(id);
    layout:setContentSize(cc.size(pageWidth, pageHeight));

    self:updateHeroModel(layout, id);

    return layout;
end

-- 重绘英雄模型
function UIHeroMain:redrawModel()
    print("redrawModel");
    local pageView = findChildByName(self.node, "CT/mid/page_view");
    pageView:setTouchEnabled(true);
    self.pageView = pageView;

    local total = self:getHeroNumber();
    if total < 1 then
        return;
    end

    -- 先清空所有页面
    pageView:removeAllPages();

    local pageWidth = 640;
    local pageHeight = 320;
    self.heros = self:getShowHero();
    local heroNum = #self.heros;

    -- 出战英雄排中间
    local midIndex = self:getPetIndex(self.curHeroId);

    self.curPageIndex = 0;

    -- 添加左边的页面
    local leftIndex = self:getLeftPageIndex(midIndex);
    if leftIndex > 0 then
        local leftPage = self:createPage(leftIndex);
        pageView:addPage(leftPage);
        self.curPageIndex = 1;
    end

    -- 添加中间的页面
    local midPage = self:createPage(midIndex);
    pageView:addPage(midPage);

    -- 添加右边的页面
    local rightIndex = self:getRightPageIndex(midIndex);
    if rightIndex > 0 then
        local rightPage = self:createPage(rightIndex);
        pageView:addPage(rightPage);
    end

    pageView:scrollToPage(self.curPageIndex);

    -- 注册翻页事件
    local function pageViewEvent(sender, eventType)
        if eventType == ccui.PageViewEventType.turning then
            self:changeArrowState();
            local curPageIndex = pageView:getCurPageIndex();
            if curPageIndex == self.curPageIndex then
                -- 喂食重新在检查下是否显示
                local foodNode = findChildByName(self.node, "CT/food_node");
                if not foodNode:isVisible() then
                    self:redrawFood()
                end

                return;
            end

            -- 更新左右页面
            self:updateLeftAndRightPage(self.curPageIndex, curPageIndex);

            -- 上面的更新可能改变索引值， 此处需要重新获取
            curPageIndex = pageView:getCurPageIndex();

            local curHeroId = sender:getPage(curPageIndex):getTag();
            self.curHeroId = curHeroId;

            -- 刷新界面
            self:redraw(true);

            EventMgr.fire(event.FEED_PET_SHOW_CHANGE, curHeroId);
        end
    end
    pageView:addEventListener(pageViewEvent)

    local moveMask = findChildByName(self.node, "CT/mid/move_mask");
    moveMask:addTouchEventListener(function(sender,eventType)
        if eventType == ccui.TouchEventType.moved then
            -- 隐藏喂食节点
            self:hideFoodNode();

            moveMask:setSwallowTouches(false);
            return false;
        else
            moveMask:setSwallowTouches(false);
            return false;
        end
    end);

end

-- 隐藏喂食节点
function UIHeroMain:hideFoodNode()
    local foodNode = findChildByName(self.node, "CT/food_node");
    foodNode:setVisible(false);
end

-- 更新左右页面
function UIHeroMain:updateLeftAndRightPage(lastSelectedIndex, curSelectedIndex)
    local curHeroId = self.pageView:getPage(curSelectedIndex):getTag();
    local midIndex = self:getPetIndex(curHeroId);

    if lastSelectedIndex < curSelectedIndex then
        -- 左移

        -- 添加右边的页面
        local needAdjust = false;
        local rightIndex = self:getRightPageIndex(midIndex);
        if rightIndex > 0 then
            local rightPage = self:createPage(rightIndex);
            self.pageView:addPage(rightPage);
        end

        -- 删除左边第一个
        self.pageView:removePageAtIndex(0);

        -- 重置当前页
        self.pageView:scrollToPage(1);
        self.curPageIndex = 1;
    else
        -- 右移

        -- 添加左边的页面
        local needAdjust = false;
        local leftIndex = self:getLeftPageIndex(midIndex);
        if leftIndex > 0 then
            local leftPage = self:createPage(leftIndex);
            self.pageView:insertPage(leftPage, 0);
            needAdjust = true;
        end

        -- 删除右边第一个
        self.pageView:removePageAtIndex(3);

        -- 重置当前页
        self.pageView:scrollToPage(1);
        self.curPageIndex = 1;
    end
end

-- 设置箭头显示/隐藏
function UIHeroMain:changeArrowState()
    local btnPrevious = findChildByName(self.node, "CT/mid/left_btn");
    local btnNext = findChildByName(self.node, "CT/mid/right_btn");

    if #self.heros <= 1 then
        self:setArrowVisible(btnNext, false);
        self:setArrowVisible(btnPrevious, false);
    else
        self:setArrowVisible(btnNext, true);
        self:setArrowVisible(btnPrevious, true);
    end
end

-- 设置按钮可见性
function UIHeroMain:setArrowVisible(btn, visible)
    btn:setVisible(visible and not self.hideArrow);
end

-- 保存设置
function UIHeroMain:saveSetting()
    -- 修改出战英雄
    Operation.cmd_activate_pet(self.curHeroId);
end

-- 显示英雄信息界面
function UIHeroMain:showHeroInfo()
    -- 没有英雄，不处理
    if not self.curHeroId then
        return;
    end

    if UIMgr.getCurrentScene():isOpen("UIHeroInfo") then
        UIMgr.getCurrentScene():removeFormByName("UIHeroInfo");
    end
    local info = {["classId"] = self.curHeroId, ["isActive"] = true, };
    local uiHeroInfo = UIHeroInfo.create(info);
    UIMgr.getCurrentScene():addForm(uiHeroInfo);
end

-- 检查出战英雄设置是否变化
function UIHeroMain:isSettingChange()
    local activeHero = ME.user:getActivePet();

    -- 当前没有出战英雄
    if not activeHero then
        return true;
    end

    return self.curHeroId ~= activeHero.classId;
end

-- 重绘喂食图标
function UIHeroMain:redrawFood()
    --print("redraw food");

    Profiler.funcBegin("UIHeroMain:redrawFood");

    if self.redrawFoodNode == nil then
        self.redrawFoodNode = cc.Node:create();
        self:addChild(self.redrawFoodNode);
    end

    -- food_panel用于播放出场动画
    -- food_node 用于播放循环动画
    local foodPanel = findChildByName(self.node, "CT/food_panel");
    local foodNode = findChildByName(self.node, "CT/food_node");
    if not FeatureItemM.isFeatureOpen("food") then
        -- 功能未激活
        foodNode:setVisible(false);
        foodPanel:setVisible(false);
        return;
    end

    -- 当前喂满不显示
    local pet = PetM.getMyPet(self.curHeroId);
    if #pet:getFeedRecord() == PetM.getMaxFeedId(pet) then
        foodNode:setVisible(false);
        foodPanel:setVisible(false);
        return;
    end

    -- 已经在播喂食动画了
    if self.isPlayFoodNodeAnim then
        return;
    end

    -- 出场动画相关配置
    local appearTime = 0.2;    -- 出场动画播放时间
    local apearScale = 0.01;   -- 出场模型比例

    -- 重复动画相关配置
    local initScale  = 0.8;    -- 初始模型比例
    local transScale = 0.85;   -- 过渡模型比例
    local finalScale = 0.9;    -- 最终模型比例
    local speedCoef =  1.0;    -- 速度系数

    -- 1.播放出场动画

    -- 统一显示蛋糕图
    local icon = ItemM.query(1204, "icon");
    local iconPath = getItemIconPath(icon);

    -- 初始化
    local foodImg = findChildByName(foodPanel, "food");
    local foodIcon = findChildByName(foodNode, "food");
    local foodTips = findChildByName(foodNode, "dialog");
    foodImg:loadTexture(iconPath);
    foodPanel:setVisible(true);
    foodNode:setVisible(false);
    foodPanel:setScale(apearScale);
    foodPanel:stopAllActions();

    local function doRedrawFood()
        self.isPlayFoodNodeAnim = true;

        local scaleUp = cc.ScaleTo:create(appearTime, initScale);
        foodPanel:runAction(scaleUp);

        local function foodNodeFunc()
            if not PetM.canFeed(self.curHeroId) then
                self.isPlayFoodNodeAnim = false;

                -- 如果不能喂食
                -- 快速操作可能导致已经变换英雄
                local petElse = PetM.getMyPet(self.curHeroId);
                if #petElse:getFeedRecord() == PetM.getMaxFeedId(petElse) then
                    return;
                end
                -- 食物缩小 不重复跳动
                foodPanel:setVisible(false);
                foodNode:setVisible(true);
                foodNode:stopAllActions();
                foodTips:stopAllActions();
                foodIcon:loadTexture(iconPath);
                foodNode:setScale(0.8);
            else
                -- 2.播放重复动画
                self.isPlayFoodNodeAnim = false;

                -- 快速操作可能导致已经变换英雄
                local petElse = PetM.getMyPet(self.curHeroId);
                if #petElse:getFeedRecord() == PetM.getMaxFeedId(petElse) then
                    return;
                end

                -- 隐藏出场模型
                foodPanel:setVisible(false);

                -- 初始化
                foodNode:setVisible(true);
                foodNode:stopAllActions();
                foodImg = findChildByName(foodNode, "food");
                foodImg:loadTexture(iconPath);
                foodNode:setScale(initScale);

                -- 开始重复动画
                -- 1.变大
                local upTime    = 0.25;
                local up2Time   = 0.25/speedCoef;
                local scaleUp   = cc.ScaleTo:create(upTime, transScale);
                local scaleUp2  = cc.ScaleTo:create(up2Time, finalScale);

                local seq1 = cc.Sequence:create(scaleUp, scaleUp2);
                local action1 = cc.Spawn:create(seq1);

                -- 2.短暂停顿
                local delayTime = 0.2/speedCoef;
                local delay = cc.DelayTime:create(delayTime);

                -- 3.变小
                local downTime  = 0.25/speedCoef;
                local down2Time = 0.25/speedCoef;
                local scaleDown = cc.ScaleTo:create(downTime, transScale);
                local scaleDown2 = cc.ScaleTo:create(down2Time, initScale);
                local seq2 = cc.Sequence:create(scaleDown, scaleDown2);
                local action2 = cc.Spawn:create(seq2);

                local sequenceNodeAction = cc.Sequence:create(action1, delay, action2);
                foodNode:runAction(cc.RepeatForever:create(sequenceNodeAction));
            end
        end
        -- 等待出场动画播放完毕后开始动画
        performWithDelay(self, foodNodeFunc, appearTime);
    end

    self.redrawFoodNode:stopAllActions();
    performWithDelay(self.redrawFoodNode, doRedrawFood, 0.5);

    Profiler.funcEnd("UIHeroMain:redrawFood");
end

-- 英雄升阶的回调
function whenUpgradeRank(self, args)
    -- 本界面没打开，不处理
    if not UIMgr.getCurrentScene():isOpen("UIHeroMain") then
        return;
    end

    -- 不是当前选择的英雄，不处理
    if self.curHeroId ~= args["class_id"] then
        return;
    end

    -- 刷新英雄星级
    self:redrawHeroStar();

    -- 刷新基础属性
    self:redrawBaseAttrib();

    -- 显示天赋信息
    self:redrawTalent();

    -- 刷新技能
    self:redrawSkill();

    -- 刷新装备和徽章
    self:redrawEquip();
end

-- 英雄升级的回调
function UIHeroMain:whenLevelUp(args)
    -- 本界面没打开，不处理
    if not UIMgr.getCurrentScene():isOpen("UIHeroMain") then
        return;
    end

    -- 不是当前选择的英雄，不处理
    if self.curHeroId ~= args["class_id"] then
        return;
    end

    -- 刷新基础属性
    self:redrawBaseAttrib();

    -- 刷新技能
    self:redrawSkill();

    -- 重绘喂食图标
    self:redrawFood();

    -- 标记喂食功能已经提醒
    if FeatureItemM.isFeatureNeedReminder("food") then
        FeatureItemM.markAsReminded("food");
    end

    -- 底部菜单更新
    UIMgr.getCurrentScene().bottomMenu:updateHeroBubble()
end

-- 英雄装备合成成功的回调
function whenEquip(self, args)
    -- 本界面没打开，不处理
    if not UIMgr.getCurrentScene():isOpen("UIHeroMain") then
        return;
    end

    local id = args;
    if EquipM.query(id, "type") == EQUIP_TYPE_COSTUME then
        local petId = EquipM.query(args, "dbase")["pet_id"];
        local layout = self.pageView:getPage(self.pageView:getCurPageIndex());
        self:updateHeroModel(layout, petId);

        self:redrawBaseAttrib();
        return;
    end

    -- 播放特效
    local pet = PetM.getMyPet(self.curHeroId);
    local weaponClassId = EquipM.getHeroEquip(pet, EQUIP_TYPE_WEAPON, 1);
    local medalClassId = EquipM.getHeroEquip(pet, EQUIP_TYPE_MEDAL, 1);
    local iconNode;
    local classId;
    if weaponClassId ~= self.lastWeaponClassId then
        iconNode = findChildByName(self.node, "BT/equip_frame/weapon_node/icon");
        classId = weaponClassId;
        -- 武器音效
        AudioM.playFx("admix_arms");
    elseif  medalClassId ~= self.lastMedalClassId then
        iconNode = findChildByName(self.node, "BT/equip_frame/medal_node/icon");
        classId = medalClassId;
        -- 勋章音效
        AudioM.playFx("admix_medal");
    end

    local function callFunc()
        -- 刷新装备和徽章
        self:redrawEquip();

        -- 刷新基础属性
        self:redrawBaseAttrib();

        -- 显示效果
        local effect = "";
        local props = EquipM.query(classId).prop;
        for i = 1, #props do
            local function show()
                local prop = props[i];
                local desc = extractString(PropM.getPropDesc(props[i]));

                local mid = findChildByName(self.node, "CT/mid");
                playAttribUpEffect3(mid, cc.p(0, 50), desc);
            end
            performWithDelay(self, show, i*0.7);
        end
    end

    callFunc();
    --  SLIMED-1103 不再播放过光的效果
    -- if iconNode and classId then
    --     playEquipWeaonAndMedalEffect(iconNode, classId, callFunc);
    -- end
end

-- 英雄佩戴技能的回调
function whenSkillOption(self, args)
    -- 本界面没打开，不处理
    if not UIMgr.getCurrentScene():isOpen("UIHeroMain") then
        return;
    end

    -- 刷新技能
    self:redrawSkill();
end

-- 英雄出战的回调
function whenPetActivate(self, classId)
    -- 提示出战成功
    alert(getLocStr("hero_setting_saved"));

    -- 判断出战英雄
    local activeHero = ME.user:getActivePet();

    -- 判断是否在出战英雄所在页
    if activeHero.classId == self.curHeroId then
        local layout = self.pageView:getPage(self.pageView:getCurPageIndex());

        -- 播放施法效果
        local animaNode = layout.animaNode;
        if animaNode ~= nil then
            animaNode:switchAction(HERO_ACTION_CAST_ME, true);
        else
            self.actionPet = self.curHeroId;
        end

        -- 隐藏出战按钮
        local btnOk = findChildByName(self.node, "BOTTOM/ok_btn");
        btnOk:setVisible(false);
    end
end

-- 根据阶位获取开启下一阶段的地牢id
function getNextDIdByRank(rank)
    local dungeonId = 999;

    if rank <= 2 then
        dungeonId = 33;
    elseif rank <= 3 then
        dungeonId = 62;
    elseif rank <= 4 then
        dungeonId = 92;
    end

    return dungeonId;
end

-- 获取宠物在可现实的宠物列表中的索引
function UIHeroMain:getPetIndex(classId)
    local heros = self:getShowHero();
    local index = 0;
    for _, id in pairs(heros) do
        index = index + 1;

        if id == classId then
            return index;
        end
    end

    return -1;
end

-- 隐藏界面主要元素
function UIHeroMain:hide()
    local function hideNode(nodeName)
        local targetNode = findChildByName(self.node, nodeName);
        targetNode:setVisible(false);
    end

    hideNode("TOP");
    hideNode("CT");
    hideNode("BT");
end

-- 显示界面元素
function UIHeroMain:show()
    local function showNode(nodeName)
        local targetNode = findChildByName(self.node, nodeName);
        targetNode:setVisible(true);
    end

    showNode("TOP");
    showNode("CT");
    showNode("BT");
end

-- 获得焦点隐藏焦点
function UIHeroMain:bottomFlyUp()
    local bottom = findChildByName(self.node, "BOTTOM");
    if bottom then
        bottom:setVisible(true);
        FormEffectM.bottomPopUpFromBottom(self);
    end
end

-- 失去焦点,隐藏Bottom
function UIHeroMain:hideBottom()
    local bottom = findChildByName(self.node, "BOTTOM");
    if bottom then
         bottom:setVisible(false);
    end
end
